using System;
using System.Collections.Specialized;
using System.Globalization;
using System.Text;

namespace flickr.api.communication
{
    public class FlickrRequest
	{
        public readonly FlickrMethod FlickrMethod;
	    public readonly NameValueCollection Parameters = new NameValueCollection();
        private const string serviceUrl = "http://www.flickr.com/services/rest/?";

	    public FlickrRequest(string method)
        {
            FlickrMethod = new FlickrMethod(method);
            Initialize();
        }

        public FlickrResponse Send(FlickrConnection connection)
        {
            return Send(connection, this);
        }

	    public static FlickrResponse Send(FlickrConnection connection, FlickrRequest request)
	    {
            return connection.SendFlickrRequest(request);
	    }

        internal string BuildRequestUrl(string apiKey, string sharedSecret, string apiToken)
        {
            StringBuilder url = new StringBuilder(serviceUrl);

            AddParameter(FlickrParameterType.ApiKey, apiKey);
            AddParameter(FlickrParameterType.AuthToken, apiToken);
            AddParameter(FlickrParameterType.ApiSignature, SignRequest(sharedSecret));

            string queryStringFormat = "{0}={1}&";
            foreach(string key in Parameters.AllKeys)
            {
                url.AppendFormat(queryStringFormat, key, Utilities.UrlEncode(Parameters[key]));
            }

            //DEBUG url.toString();
            return url.ToString();
        }

	    private string SignRequest(string sharedSecret)
	    {
            if( sharedSecret == string.Empty || sharedSecret.Length == 0 )
            {
                throw new ArgumentNullException("Shared Secret cannot be Empty");
            }

	        StringBuilder signatureString = new StringBuilder(sharedSecret);
            string[] keys = Parameters.AllKeys;
            Array.Sort(keys);
	        foreach (string key in keys)
	        {
	            signatureString.Append(key + Parameters[key]);
	        }

	        return Utilities.Md5Hash(signatureString.ToString());
	    }

	    private void Initialize()
        {
            AddParameter(FlickrMethod);
        }

	    public void Reset()
        {
            Parameters.Clear();
            Initialize();
        }

        private void AddParameter(FlickrMethod method)
        {
            AddParameter(FlickrParameterType.Method, method.Name);
        }
	    
	    private void AddParameter(string name, string value)
	    {
	        Parameters.Add(name, value);
	    }

	    public void AddParameter(FlickrParameterType param, object value)
	    {
	        if (value != null)
                AddParameter(param.Name, value.ToString());    
	    }

        public void AddParameter(FlickrParameterType param, string value)
        {
            if (value != null && value.Length > 0)
            {  
                AddParameter(param.Name, value);
            }
        }

        public void AddParameter(FlickrParameterType name, DateTime value)
        {
	        if (value > DateTime.MinValue && value < DateTime.MaxValue)
	        {
	            AddParameter(name, value.ToString("yyyy-MM-dd hh:mm:ss", DateTimeFormatInfo.InvariantInfo));
	        }
        }

        public void AddParameter(FlickrParameterType name, int value)
        {
	        if (value > int.MinValue && value < int.MaxValue)
	        {
	            AddParameter(name, value.ToString());
	        }
        }

        public void AddParameter(FlickrParameterType name, long value)
        {
            if (value > long.MinValue && value < long.MaxValue)
            {
                AddParameter(name, value.ToString());
            }
        }

        public void AddParameter(FlickrParameterType name, bool value)
        {
            AddParameter(name, value ? "1" : "0");
        }

        public void AddParameter(FlickrParameterType name, string[] values)
	    {
	        StringBuilder concatedParameter = new StringBuilder();
	        foreach(string value in values)
	        {
                concatedParameter.AppendFormat("{0},", value);
	        }
	        AddParameter(name, concatedParameter.ToString().Substring(0, concatedParameter.Length - 1));
	    }
	    
	    public void AddParameter(FlickrParameter parameter)
	    {
            AddParameter(parameter.Name, parameter.Value);
	    }

	    public void AddParameters(FlickrParameter[] parameters)
	    {
	        foreach (FlickrParameter parameter in parameters)
	        {
                AddParameter(parameter);
	        }
	    }
	}
}
